/**
  ******************************************************************************
  * @file    main.c
  * @author  ashuai0110
  * @version V2.0
  * @date    2024-09-08
  * @brief   使用举例-bootloader(基于ymodem协议传输升级文件)
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "bsp.h"

#include "ymodem.h"
#include "bootloader.h"

/* 私有宏定义-----------------------------------------------------------------*/

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/
static uint8_t ymodemBuf[YMODEM_BUF_1K_MAX]; /* ymodem数据缓存区 */
ymodem_t ymodem; /* ymodem实例 */
file_info_t fileInfo; /* 文件信息结构体 */

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/


/**
  * @brief  This function handles SysTick Handler.
  *
  * @param  None
  *
  * @retval None
  */
void SysTick_Handler(void)
{
    /* 组件计时节拍接口 */
    mw_tick_inc(1);
}

/**
  * @brief  串口1中断
  */
void USART1_IRQHandler(void)
{
    static uint8_t rxData, txData, ret;
    
    /* 串口接收中断 */
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        rxData = USART_ReceiveData(USART1);
        /* 写入接收缓存 */
        ret = ymodem_write_rx_buf(&ymodem, &rxData, 1);
        if(0 == ret)
        {
            MW_WARN_LOG("ymodem_write_rx_buf failure");
        }
        
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
    /* 串口发送中断 */
    if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
    {
        /* 读出发送缓存 */
        ret = ymodem_read_tx_buf(&ymodem, &txData, 1);
        if(1 == ret)
        {
            USART_SendData(USART1, txData);
        }
        
        USART_ClearITPendingBit(USART1, USART_IT_TXE);
    }
}

/**
  * @brief  获取ymodem数据包回调函数
  *
  * @param  packetNum : 数据包序号 首次从1开始 超出255回到0
  *
  * @param  data      : 有效数据缓冲区(需读出数据)
  *
  * @param  len       : 有效数据长度(包含填充内容0x1A)
  *
  * @retval None
  */
void ymodem_get_packet_callback(uint8_t packetNum, uint8_t *data, uint16_t *len)
{
    /* 文件信息 */
    if(ymodem_get_file_flag(&ymodem))
    {
        ymodem_get_file_info(&ymodem, &fileInfo); /* 获取文件信息保存至fileInfo结构体 */
        /* 用户可以自行校验文件名称 不再举例 */
        MW_DEBUG_LOG("ymodem rec file name:%s, size:%d Byte", fileInfo.fileName, fileInfo.fileSize);
        /* 擦除应用程序FLASH区域 */
        if(0 != boot_erase_application(fileInfo.fileSize))
        {
            MW_ERROR_LOG("bootloader erase application error!");
        }
    } /* 数据 */
    else
    {
        boot_program_application((void *)data, *len); /* 编程应用程序FLASH区域 */
    }
}

/**
  * @brief  恢复/失能已使用的外设/中断
  *
  * @param  None
  *
  * @retval None
  */
void boot_recover_all(void)
{
    GPIO_DeInit(GPIOA);
    USART_DeInit(USART1);
}

/**
  * @brief  main
  */
int main(int argc, char *argv[])
{
    MW_VER_LOG("example-bootloader", 2, 0, 0);

    /* bootloader初始化 对接MCU FLASH操作接口 */
    boot_init(bsp_flash_erase, bsp_flash_read, bsp_flash_write);
    /* 检查是升级还是跳转APP */
    if(MW_RET_ERR == boot_check_updata_flag())
    {
        MW_ERROR_LOG("jump error, please check the program!");
        return 0;
    }

    /* BSP初始化 */
    bsp_init();

    /* ymodem组件初始化 超时时间10000ms(此处为便于操作串口软件进行测试故设置较长时间) 重发次数2次 */
    ymodem_init(&ymodem, ymodemBuf, sizeof(ymodemBuf), bsp_usart1_tx_en, ymodem_get_packet_callback, NULL, 10000, 2);
    /* 启动ymodem接收 */
    ymodem_rx_start(&ymodem, YMODEM_RX_FLAG_CRC);
    
    while(1)
    {
        static uint8_t ret;
        
        ret = ymodem_rx_poll(&ymodem); /* ymodem接收轮询处理 */
        /* 传输完成则跳转到应用程序 */
        if(MW_RET_OK == ret)
        {
            MW_DEBUG_LOG("boot jump to application");
            ret = boot_to_application(); /* 跳转到应用程序 */
            /* 若跳转应用程序失败则跳转到备份(出厂)程序 */
            if(MW_RET_ERR == ret)
            {
                MW_ERROR_LOG("boot jump to application failure!");
                ret = boot_to_factory(); /* 跳转到备份(出厂)程序 */
                if(MW_RET_ERR == ret)
                {
                    MW_ERROR_LOG("boot jump to factory failure!");
                }
            }
        } /* 传输失败或传输超时则跳转到备份(出厂)程序 */
        else if(MW_RET_ERR == ret || MW_RET_TIMEOUT == ret)
        {
            MW_DEBUG_LOG("boot jump to factory");
            ret = boot_to_factory(); /* 跳转到备份(出厂)程序 */
            if(MW_RET_ERR == ret)
            {
                MW_ERROR_LOG("boot jump to factory failure!");
            }
        }
    }
}

